-- a class Account

-- encapsulation
Account = {balance = 0}

function Account:new (o)
	o = o or {}
	self.__index = self
	setmetatable(o, self)
	return o
end

function Account.withdraw (self, v)
	if v > self.balance then
		error("insufficient funds")
	end
	self.balance = self.balance - v
end

function Account.deposit (self, v)
	self.balance = self.balance + v
end

-- a instance of class Account
local a = Account:new{balance = 0}
a:deposit(100.00)
print(a.balance) 									-- 100
a:withdraw(50.00)
print(a.balance) 									-- 50

-- inheritance
SpecialAccount = Account:new()

function SpecialAccount:withdraw (v)
	if v - self.balance >= self:getLimit() then
		error("insufficient funds")
	end
	self.balance = self.balance - v 
end

function SpecialAccount:getLimit ()
	return self.limit or 0
end

s = SpecialAccount:new{limit = 1000.00}
s:deposit(100.00)
print(s.balance, s.limit) 	-- 100, 1000
s:withdraw(200.00)
print(s.balance, s.limit) 	-- -100, 1000

-- privacy
function newAccount (initialBalance)
	local self = {balance = initialBalance}

	local withdraw = function (v)
		self.balance = self.balance - v
	end

	local deposit = function (v)
		self.balance = self.balance + v
	end

	local getBalance = function ()
		return self.balance 
	end

	return {
		withdraw = withdraw,
		deposit = deposit,
		getBalance = getBalance
	}
end

acc1 = newAccount(100)
acc1.withdraw(40)
print(acc1.getBalance())
acc1.deposit(400)
print(acc1.getBalance())

-- single-method object
function newObject (value)
	return function (action, v)
		if action == "get" then return value
		elseif action == "set" then value = v
		else error("invalid action")
		end
	end
end

d = newObject(0)
print(d("get")) 	-- 0
d("set", 10)
print(d("get")) 	-- 10
